# estTable.R
# created 2012 July 21

estTable <- function(
  varNames, 
  treatment      = 'HSgrad', 
  objectEnvir, 
  objectSuffix   = '.IV', 
  noCoefIfNoSE   = TRUE, 
  clusterSEs     = FALSE) {
  # Returns a matrix of estimates and standard errors.  Every entry is an  
  # estimate for the effect of treatment or the standard error for such an 
  # estimate.  Each row corresponds to a different model, each column to a 
  # different dependent outcome.  [2012 07 21]
  
  # The row entries are given in terms of alphabetical order of the model.
  # names For example, if objectEnvir contains models eqwlth.IV1 and 
  # eqwlth.IV2, the estimate and SE from eqwlth.IV1 will be given before the 
  # estimate and SE for eqwlth.IV2.  [2012 07 26]
  
  # In some cases, the objects in objectEnvir will have estimates but no 
  # corresponding standard errors.  (With ivreg(), this seems to happen in  
  # cases of linear dependence: ivreg() doesn't know what variables to drop.)   
  # In these cases, noCoefIfNoSE = TRUE causes the output to list NA for the  
  # estimate as well as the standard error.  [2012 07 21]

  require(ivpack)  # for cluster.robust.se
  require(lmtest)  # for coeftest()
  source('functions/getClusters.R')
  
  if (! class(varNames) %in% 'character') {
    stop("varNames argument must be of class 'character'.")
  }
  if (! class(treatment) %in% 'character') {
    stop("treatment argument must be of class 'character'.")
  }
  
  ncol     <- length(varNames) * 2
  nrow     <- length(ls(pat = paste0('^', varNames[1], '\\.'), envir = objectEnvir))
  colNames <- paste0(rep(varNames, each = 2), c('', '.se')) 
  out <- matrix(NA, nrow = nrow, ncol = ncol, dimnames = list(NULL, colNames))
  
  
  # Append 'TRUE' to end of treatment name if necessary.  [2012 12 04]
  IVObjectNames <- sort(ls(pat = paste0('^', varNames[1]), envir = objectEnvir))
  IVObject      <- get(IVObjectNames[1], envir = objectEnvir)
  testNames     <- names(IVObject$coefficients)
  if (!any(treatment%in%testNames) & !any(paste0(treatment, 'TRUE')%in%testNames)) {
    stop(paste0('treatment "', treatment, '" does not seem to be in at least one of the model objects.'))
  }
  else if (!any(treatment%in%testNames) & any(paste0(treatment, 'TRUE')%in%testNames)) {
    treatName <- paste0(treatment, 'TRUE')
  }
  else { 
    treatName <- treatment
  }
  
  # Create the table
  for (varName in varNames) {
    IVObjectNames <- sort(ls(pat = paste0('^', varName, objectSuffix), envir = objectEnvir))
    IVObjects <- lapply(IVObjectNames, function (x) get(x, envir = objectEnvir))
    if (clusterSEs) out[, c(varName, paste0(varName, '.se'))] <- t( mapply(IVObjects, FUN = function (x) cluster.robust.se(x, getClusters(x))[treatName, c('Estimate', 'Std. Error')] ) )
    else            out[, c(varName, paste0(varName, '.se'))] <- t( mapply(IVObjects, FUN = function (x) coeftest(x)                         [treatName, c('Estimate', 'Std. Error')] ) )
  }
  if (noCoefIfNoSE) {
    SECols             <- seq(2, ncol, by = 2)
    if (nrow(out) == 1) {
      missingSELocations <- sapply(out[, SECols], is.na)      
    }
    else {
      missingSELocations <- apply(as.matrix(out[, SECols]), 2, is.na)  # the as.matrix() wrapper makes the function work when there is only one column in out[, SECols]
    }
    out[, SECols - 1][missingSELocations] <- NA
  }

  class(out) <- c(class(out), 'estTable')
  out
}

# Set up print method for estTable, so that print(estTable) doesn't list 
# attributes at the bottom.  [2012 08 01]
print.estTable <- prmatrix